package net.jxta.myjxta.plugins.filetransfer;

import net.jxta.document.AdvertisementFactory;
import net.jxta.id.IDFactory;
import net.jxta.myjxta.plugin.IPluginNotificationHandler;
import net.jxta.myjxta.plugin.ISelectableNode;
import net.jxta.myjxta.plugin.PluginBase;
import net.jxta.myjxta.plugin.PluginContainer;
import net.jxta.myjxta.util.Group;
import net.jxta.myjxta.util.Resources;
import net.jxta.myjxta.util.objectmodel.JxtaNode;
import net.jxta.myjxta.util.objectmodel.PeerNode;
import net.jxta.peergroup.PeerGroup;
import net.jxta.pipe.PipeID;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.socket.JxtaServerSocket;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;

public final class FileTransferPlugin extends PluginBase implements IPluginNotificationHandler, PluginContainer.IPopupProvider {

    final HashMap<PeerGroup, JxtaServerSocket> groupsWithListeners = new HashMap<PeerGroup, JxtaServerSocket>();
    private static final String G_TRANSFER_ID = "TRANS";
    private final ArrayList<Group> m_joinedGroups = new ArrayList<Group>();
    private final HashMap<Group, FileReceiver> group2Receiver = new HashMap<Group, FileReceiver>();

    public FileTransferPlugin() {
        super();
        setName("File Transfer");
    }


    public void init(PluginContainer c) {
        super.init(c);    //To change body of overridden methods use File | Settings | File Templates.
    }


    public void start() {
        super.start();

        //we want a simple test menu entry on the peer nodes
        m_container.registerPopupProvider(this);

        //start can be called multiple times.. maybe we already have some joined groups... start listeners for em
        for (Group group : m_joinedGroups) {
            startListener(group);
        }
    }

    public void stop() {
        m_container.removePopupProvider(this);
        for (Group group : m_joinedGroups) {
            stopListener(group);
        }
        super.stop();
    }

    public void destroy() {
        if (m_running) {
            stop();
        }
        super.destroy();
    }

    private void stopListener(Group p_group) {
        FileReceiver receiver = group2Receiver.get(p_group);
        receiver.shutdownListener();
    }

    private void startListener(Group p_group) {
        PipeAdvertisement pipeAdvertisement = getAdvertisment(p_group.getPeerGroup(), p_group.getOwnPeersCommandId() + G_TRANSFER_ID);
        FileReceiver listener = new FileReceiver(p_group.getPeerGroup(), (PipeID) pipeAdvertisement.getPipeID()) {

        protected File getTargetFile(String fileName, long fileSize, String sender) {
            File file;
            JFileChooser saveFile = new JFileChooser();
            saveFile.setSelectedFile(new File(fileName));
            saveFile.setDialogTitle("Incoming File Transfer from " + sender);
            int result = saveFile.showSaveDialog((Component) (m_container.getMyJxta() != null ? m_container.getMyJxta().getView() : null));
            file = saveFile.getSelectedFile();
            if (result != JFileChooser.APPROVE_OPTION) {
                file = null;
            }
            return file;
        }

        protected void info(String message) {
            m_container.getMyJxta().getView().showMessageDialog(message);
        }
    };
        group2Receiver.put(p_group, listener);
        listener.start();
    }


    public static PipeAdvertisement getAdvertisment(PeerGroup pg, String generatorSeed) {
        PipeAdvertisement pa = (PipeAdvertisement) AdvertisementFactory.
                newAdvertisement(PipeAdvertisement.getAdvertisementType());

        try {
            pa.setPipeID(IDFactory.newPipeID(pg.getPeerGroupID(), createMD5(generatorSeed)));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            return null;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        pa.setName(generatorSeed);
        pa.setType(PipeAdvertisement.getAdvertisementType());
        return pa;
    }

    private static byte[] createMD5(String seed) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        md5.reset();
        md5.update(seed.getBytes("UTF-8"));
        return md5.digest();
    }

    public IPluginNotificationHandler getPluginNotificationHander() {
        return this;
    }

    public void groupJoined(Group p_group) {
        if (!p_group.isVisible())
            return;

        m_joinedGroups.add(p_group);

        if (isRunning()) {
            startListener(p_group);
        }
    }

    public void groupResigned(Group p_group) {
        if (!p_group.isVisible())
            return;

        m_joinedGroups.remove(p_group);
        if (isRunning()) {
            stopListener(p_group);
        }
    }


    public void groupStateChanged(Group p_group) {
    }


    public void popupRequested(PluginContainer.IPopupGenerator popupGenerator, ISelectableNode[] selectedNodes, MouseEvent triggerEvent) {
        if (selectedNodes != null && selectedNodes.length >= 1) {
            JxtaNode jxtaNode = selectedNodes[0].getJxtaNode();
            if (jxtaNode instanceof PeerNode) {
                PeerNode peerNode = (PeerNode) jxtaNode;
                PluginContainer.MenuPath peerPath = new PluginContainer.MenuPath(Resources.getStrings().getString("menu.peer"), KeyEvent.VK_P);
                PipeID p_targetCommandPipeID = (PipeID) peerNode.getPeer().getPipeAdvertisement().getID();
                PeerGroup pg = peerNode.getParent().getGroup().getPeerGroup();
                PipeAdvertisement socketAdv = getAdvertisment(pg, p_targetCommandPipeID + G_TRANSFER_ID);
                popupGenerator.addPopup(new PluginContainer.MenuPath[]{peerPath}, 9, new BandwidthTestAction("Send File", pg, (PipeID) socketAdv.getPipeID()));
            }
        }
    }

    private final class BandwidthTestAction extends AbstractAction implements PropertyChangeListener {
        private final PipeID targetPipeID;
        private final PeerGroup peerGroup;

        public BandwidthTestAction(String name, PeerGroup pg, PipeID p_message) {
            super(name);
            peerGroup = pg;
            targetPipeID = p_message;
        }

        private File getFile() {
            JFileChooser chooser = new JFileChooser();
            chooser.showOpenDialog((Component) m_container.getMyJxta().getView());
            return chooser.getSelectedFile();
        }

        public void actionPerformed(ActionEvent e) {
            File file = getFile();
            if (file == null || !file.exists() || !file.canRead())
                return;

            FileSender tmp;
            try {
                tmp = new FileSender(peerGroup, targetPipeID, file);
                tmp.addPropertyChangeListener(this);
                tmp.start();
            } catch (FileNotFoundException e1) {
                e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }

        }

        public void propertyChange(PropertyChangeEvent evt) {
            System.out.println(evt.getPropertyName() + ":" + evt.getNewValue());
        }
    }

}
